//运行条件：项目/属性/高级/使用多字节字符集
#pragma warning(disable:4996)//防止vs2019报错，使程序顺利运行
#pragma comment(lib,"Winmm.lib")
#include <stdio.h>
#include <graphics.h>
#include <math.h>
#include <conio.h>
#include <stdlib.h>
#include "EasyXPng.h"
#include <mmsystem.h>
#define width 840
#define height 700//游戏画面尺寸
#define towernum 9//防御塔数量

int gif = 0;//这个变量老管用了
TCHAR enemy[10];//文字输出
IMAGE mainmenu/*主菜单*/, door_l, door_r, pause, victory, lose;
int enemynum = 0;
int musicon = 1;

//声明函数
void Restart(), Quit(), Door(), Pause(), Victory(), Lose(), Playmusic();
//两点逼近
void BiJin(float* x1, float* y1, float* x2, float* y2, float* speed)
{
	float speedx = *speed, speedy = *speed, dis, disy = *y2 - *y1, disx = *x2 - *x1;
	if (disx != 0 && disy != 0)
	{
		dis = sqrt(disy * disy + disx * disx);
		speedy = fabs(disy * *speed / dis);
		speedx = fabs(disx * *speed / dis);
	}
	if (speedx == 0)
		speedx = *speed;
	if (speedy == 0)
		speedy = *speed;
	if (disx > 0)
		*x1 += speedx;
	else
		*x1 -= speedx;
	if (disy > 0)
		*y1 += speedy;
	else
		*y1 -= speedy;
}
//攻击动画
class WEAPON
{
public:
	IMAGE image;
	float x, y;
	char imagename[20];
	int num, hp, flyingtime;
	void Draw()
	{
		sprintf(imagename, "weapon_%d.png", num);
		if (num == 2)
		{
			sprintf(imagename, "weapon_%d_%d.png", num, gif / 100 % 2);
			if (hp <= 0)
				sprintf(imagename, "weapon_%d_2.png", num);
		}
		loadimage(&image, imagename);
		putimagePng(x, y, &image);
	}
};
//防御塔相关
class TOWER
{
public:
	IMAGE image;//相关图片
	int value, price, num, lev, attacking, time;//相关数据
	float ATK, radius, ATKS;//防御塔属性
	float x, y;//坐标
	WEAPON weapon;
	// 防御塔附属物——防御塔菜单相关
	IMAGE menucircle, towerUI[2], guide;
	int menuopen, guideshow[8];//判断变量
	char imagename[20], guidename[20], filename[20];//文件名
	void Update()//防御塔发生变动时调用，更新防御塔属性
	{
		sprintf(filename, "towerdata_%d_%d.txt", num, lev);
		FILE* ftower = fopen(filename, "r");
		fscanf(ftower, "%d %d %f %f %f ", &price, &value, &ATK, &radius, &ATKS);
		fclose(ftower);
		ATK += rand() % 4;
		if (num == 2)
			weapon.hp = lev * 50;
	}
	void Draw()//绘制防御塔
	{
		if (lev != 0)
			sprintf(imagename, "tower_%d_%d.png", num, lev);//格式转换
		else
			sprintf(imagename, "notower.png");
		loadimage(&image, imagename);//导入图片
		putimagePng(x - 36, y - 21, &image);//防御塔图片
	}
	void Menu()//绘制菜单
	{
		if (menuopen == 1)//判断防御塔菜单是否应打开
		{
			putimagePng(x - 50, y - 50, &menucircle);
			if (lev == 0)
			{
				for (int i = 1; i <= 4; i++)
				{//掩码图绘制
					putimage(x - 22 + 38 * pow(-1, i), y - 22 + 38 * pow(-1, (i + 1) / 2), 46, 41, &towerUI[0], 46 * (i - 1), 0, SRCAND);
					putimage(x - 22 + 38 * pow(-1, i), y - 22 + 38 * pow(-1, (i + 1) / 2), 46, 41, &towerUI[1], 46 * (i - 1), 0, SRCPAINT);
				}
			}
			else if (lev < 3)
			{
				putimage(x - 23, y - 68, 46, 41, &towerUI[0], 46 * 4, 0, SRCAND);
				putimage(x - 23, y - 68, 46, 41, &towerUI[1], 46 * 4, 0, SRCPAINT);
				putimage(x - 21, y + 27, 46, 41, &towerUI[0], 46 * 6, 0, SRCAND);
				putimage(x - 21, y + 27, 46, 41, &towerUI[1], 46 * 6, 0, SRCPAINT);
			}
			else
			{
				putimage(x - 23, y - 68, 46, 41, &towerUI[0], 46 * 5, 0, SRCAND);
				putimage(x - 23, y - 68, 46, 41, &towerUI[1], 46 * 5, 0, SRCPAINT);
				putimage(x - 21, y + 27, 46, 41, &towerUI[0], 46 * 6, 0, SRCAND);
				putimage(x - 21, y + 27, 46, 41, &towerUI[1], 46 * 6, 0, SRCPAINT);
			}
			//显示防御塔UI
			for (int i = 1; i <= 4; i++)
				if (guideshow[i] == 1)
				{
					if (x + 40 * pow(-1, i) >= 610)
						putimagePng(x + 15 + 38 * pow(-1, i) - 257, y + 10 + 38 * pow(-1, (i + 1) / 2), &guide);
					else if (y >= 484)
						putimagePng(x + 15 + 38 * pow(-1, i), y + 10 + 38 * pow(-1, (i + 1) / 2) - 190, &guide);
					else
						putimagePng(x + 15 + 38 * pow(-1, i), y + 10 + 38 * pow(-1, (i + 1) / 2), &guide);
				}
			if (guideshow[5] == 1)
				if (x <= 400)
					putimagePng(x + 13, y - 34, &guide);
				else
					putimagePng(x - 244, y - 34, &guide);
		}
	}
};
//怪物相关
class ENEMY
{
public:
	IMAGE image, hp;
	int num, attacted, value, aim;//相关数据
	float x, y;//怪物坐标
	float HP, Hp, ATK, DEF, speed;//怪物属性
	float w, h;//图片长宽
	char imagename[20];//图片名
	void Update()
	{
		char filename[20];
		sprintf(filename, "enemydata_%d.txt", num);
		FILE* fenemy = fopen(filename, "r");
		fscanf(fenemy, "%d %f %f %f %f %f", &value, &Hp, &HP, &ATK, &DEF, &speed);
		fclose(fenemy);
	}
	void Draw()
	{
		loadimage(&hp, "HP.png", Hp, 3);
		sprintf(imagename, "enemy_%d_%d.png", num, gif / 100 % 2);
		loadimage(&image, imagename);
		w = image.getwidth();
		h = image.getheight();
		putimagePng(x - w / 2, y - h, &image);
		putimage(x - Hp / 4, y - h - 10, Hp / 2, 3, &hp, (Hp - HP) / 2, 0);
	}
};
//关卡地图
class MAP
{
public:
	IMAGE image;//背景						
	int lev, money, heart, enemywave, kill;
	float spot_x[10], spot_y[10];//地图关键点，怪物移动路线
	char ht[2], my[4], ew[10];//文字输出相关
	TOWER tower[towernum];//防御塔数量
	ENEMY enemy[99];//敌人
	char imagename[20], mapname[20], spotname[20];//文件名
	void Start()
	{
		sprintf(imagename, "level_%d.png", lev);
		sprintf(mapname, "level_%d.txt", lev);//格式转换实现批量导入
		sprintf(spotname, "aim_%d.txt", lev);
		loadimage(&image, imagename);
		money = 320;
		heart = 2;
		kill = 0;
		enemynum = 0;//数据重置
		FILE* fmap = fopen(mapname, "r");//从文件中读取防御塔坐标
		FILE* faim = fopen(spotname, "r");
		for (int i = 0; i < towernum; i++)
		{
			loadimage(&tower[i].menucircle, "menucircle.png", 100, 100);
			loadimage(&tower[i].towerUI[0], "towerUIy.png", 322, 41);
			loadimage(&tower[i].towerUI[1], "towerUI.png", 322, 41);
			tower[i].lev = 0;
			fscanf(faim, "%f %f ", &spot_x[i], &spot_y[i]);
			fscanf(fmap, "%f %f ", &tower[i].x, &tower[i].y);//读取坐标
			tower[i].weapon.x = tower[i].x;
			tower[i].weapon.y = tower[i].y;
		}
		for (int i = 0; i < 99; i++)
		{
			enemy[i].aim = 0;
			enemy[i].x = spot_x[0];
			enemy[i].y = spot_y[0];
		}
		fclose(fmap);//关闭文件
		fclose(faim);
		BeginBatchDraw();
	}	//绘制地图函数
	void Draw()//绘制地图//主要绘制函数
	{
		cleardevice();//清屏
		putimage(0, 0, &image);//背景
		for (int i = 0; i < towernum; i++)
			tower[i].Draw();//绘制防御塔
		for (int i = 0; i < towernum; i++)
			if (tower[i].attacking == 1 && tower[i].lev > 0)
				tower[i].weapon.Draw();
		for (int i = 0; i < enemynum; i++)
			if (enemy[i].HP > 0)
				enemy[i].Draw();//绘制怪物	

		for (int i = 0; i < towernum; i++)
			tower[i].Menu();//绘制防御塔菜单
	}
	void Update()
	{
		if (gif % 500 == 0 && enemynum <= 99)
		{
			enemynum++;
			enemy[enemynum].num = 1;
			if (enemynum % 10 == 0)
				enemy[enemynum].num = 2;
			if (enemynum % 30 == 0)
				enemy[enemynum].num = 3;
			enemy[enemynum].Update();//新入场的怪物并对其更新
		}
		for (int i = 0; i < enemynum; i++)
		{
			if (enemy[i].HP > 0)//仅在怪物存活的情况下进行
			{
				BiJin(&enemy[i].x, &enemy[i].y, &spot_x[enemy[i].aim], &spot_y[enemy[i].aim], &enemy[i].speed);
				if (fabs(enemy[i].x - spot_x[enemy[i].aim]) < 1 + rand() % 3 && fabs(enemy[i].y - spot_y[enemy[i].aim]) < 1 + rand() % 3)
					enemy[i].aim++;//朝下一个目标迈进
				if (fabs(enemy[i].x - spot_x[7]) < 5 && fabs(enemy[i].y - spot_y[7]) < 5)
					heart--;
			}
		}
		for (int i = 0; i < towernum; i++)
		{//对防御塔和怪物进行遍历
			for (int j = 0; j < enemynum; j++)
			{
				float x = enemy[j].x - enemy[j].w / 2, y = enemy[j].y - enemy[j].h;
				if (tower[i].lev == 0)
					tower[i].attacking = 0;
				if (enemy[j].HP > 0 && tower[i].lev > 0 && tower[i].num != 2)
				{
					if (sqrt((tower[i].x - x) * (tower[i].x - x) + (tower[i].y - y) * (tower[i].y - y)) < tower[i].radius)
					{//当怪物进入攻击范围时
						tower[i].attacking = 1;
						enemy[j].attacted = 1;
					}
					if (sqrt((tower[i].x - x) * (tower[i].x - x) + (tower[i].y - y) * (tower[i].y - y)) > tower[i].radius)
						enemy[j].attacted = 0;
					if (tower[i].attacking == 1 && enemy[j].attacted == 1)//当防御塔在攻击且怪物正受到攻击时
						BiJin(&tower[i].weapon.x, &tower[i].weapon.y, &x, &y, &tower[i].ATKS);
					if (fabs(tower[i].weapon.x - x) <= enemy[j].w / 2 && fabs(tower[i].weapon.y - y) <= enemy[j].h / 2)
					{//当防御塔的攻击与怪物发生有效碰撞时
						tower[i].weapon.x = tower[i].x;
						tower[i].weapon.y = tower[i].y - 12;
						enemy[j].HP -= tower[i].ATK - enemy[j].DEF;
						if (enemy[j].HP <= 0)
						{
							kill++;
							money += enemy[j].value + rand() % 4;
							enemy[j].attacted = 0;
						}
					}
					if (tower[i].num == 2 && tower[i].weapon.hp <= 0)
					{
						tower[i].time++;
						if (tower[i].time == 20000)
							tower[i].weapon.hp = 50 * tower[i].lev;
					}

				}
			}
		}
	}
} map;
//初始化、导入资源
void Start()
{
	//全局资源导入
	loadimage(&pause, "pause.png", 428, 312);
	loadimage(&door_l, "doorl.png", width / 2, height);
	loadimage(&door_r, "doorr.png", width / 2, height);
	loadimage(&mainmenu, "mainmenu.png");
	loadimage(&victory, "victory.png", 384, 384);
	loadimage(&lose, "lose.png", 384, 384);
	initgraph(width, height);
	Playmusic();
	setbkmode(TRANSPARENT);//所有文字的透明输出
	BeginBatchDraw();//开始批量绘制
}
//与输入无关的更新
void UpdateWithoutInput()
{
	if (map.kill == 99 && map.heart > 0)
		Victory();
	if (map.heart <= 0)
		Lose();
	map.Update();
	map.Draw();
	settextstyle(40, 0, _T("Impact"));
	sprintf(map.ht, _T("%d"), map.heart);
	outtextxy(40, 0, map.ht);
	sprintf(map.my, _T("%d"), map.money);
	outtextxy(160, 0, map.my);
	sprintf(map.ew, _T("WAVE %d/16"), enemynum / 6);
	outtextxy(60, 40, map.ew);

}
//与输入有关的更新
void UpdateWithInput()
{//鼠标操作及对应判定变量的改变
	if (MouseHit())
	{
		MOUSEMSG m = GetMouseMsg();//获取鼠标信息
		for (int i = 0; i < towernum; i++)
		{
			if (map.tower[i].menuopen == 0)//防御塔菜单未打开时
			{
				if (fabs(m.x - map.tower[i].x) <= 36 && fabs(m.y - map.tower[i].y) <= 21
					&& fabs(m.x - 0) >= 36 && fabs(m.y - 0) >= 21 && m.uMsg == WM_LBUTTONDOWN)
				{
					//关闭其他菜单，仅打开此菜单
					for (int j = 0; j < towernum; j++)
					{
						if (j == i)
							map.tower[j].menuopen = 1;
						else
							map.tower[j].menuopen = 0;
					}
				}
			}
			else
			{
				if (map.tower[i].lev == 0)
				{
					for (int j = 1; j <= 4; j++)
						if (fabs(m.x - map.tower[i].x - 40 * pow(-1, j)) < 20 && fabs(m.y - map.tower[i].y - 40 * pow(-1, (j + 1) / 2)) < 20)
						{
							if (map.tower[i].guideshow[j] == 1 && m.uMsg == WM_LBUTTONDOWN)
							{
								map.tower[i].weapon.num = map.tower[i].num = j;//确定防御塔编号
								map.tower[i].lev++;//防御塔升级
								map.tower[i].Update();
								if (map.money >= map.tower[i].price)
								{
									map.tower[i].weapon.x = map.tower[i].x;
									map.tower[i].weapon.y = map.tower[i].y;
									map.tower[i].menuopen = 0;
									map.money -= map.tower[i].price;//消耗金币
								}
								else
									map.tower[i].lev--;
							}
							else
							{
								sprintf(map.tower[i].guidename, "guide_%d_1.png", j);
								loadimage(&map.tower[i].guide, map.tower[i].guidename, 230, 166);
								for (int k = 1; k <= 4; k++)
									if (k == j)
										map.tower[i].guideshow[k] = 1;
									else
										map.tower[i].guideshow[k] = 0;
							}
						}
						else
							map.tower[i].guideshow[j] = 0;
				}
				else
				{
					if (fabs(m.x - map.tower[i].x) < 20 && fabs(m.y - map.tower[i].y + 50) < 20 && map.tower[i].lev < 3)
					{
						if (map.tower[i].guideshow[5] == 1 && m.uMsg == WM_LBUTTONDOWN)
						{
							map.tower[i].lev++;
							map.tower[i].Update();
							if (map.money >= map.tower[i].price)
							{
								map.tower[i].lev++;
								map.money -= map.tower[i].price;
							}
							else
							{
								map.tower[i].lev--;
								map.tower[i].Update();
							}
						}
						else
						{
							sprintf(map.tower[i].guidename, "guide_%d_%d.png", map.tower[i].num, map.tower[i].lev + 1);
							loadimage(&map.tower[i].guide, map.tower[i].guidename, 230, 166);
							map.tower[i].guideshow[5] = 1;
						}
					}
					else if (fabs(m.x - map.tower[i].x) < 13 && fabs(m.y - map.tower[i].y - 50) < 13 && m.uMsg == WM_LBUTTONDOWN)
					{
						map.money += map.tower[i].value;
						map.tower[i].lev = 0;
					}
					else
						map.tower[i].guideshow[5] = 0;
				}
				if (m.uMsg == WM_LBUTTONDOWN)
				{
					map.tower[i].menuopen = 0;
					for (int j = 0; j < 8; j++)
						map.tower[i].guideshow[j] = 0;
				}
			}
		}
		if (m.uMsg == WM_LBUTTONDOWN && fabs(m.x - 786) <= 14 && fabs(m.y - 53) <= 13)
			Pause();
	}
}
//播放音乐
void Playmusic()
{
	mciSendString("open bkmusic.mp3 alias bkmusic", NULL, 0, NULL);
	mciSendString("play bkmusic", NULL, 0, NULL);
}
//界面切换动画
void Door()
{
	for (int i = width; i >= width / 2; i -= 2)
	{
		cleardevice();
		putimage(i, 0, &door_r);
		putimage(width / 2 - i, 0, &door_l);
		FlushBatchDraw();
	}
	Sleep(800);
	for (int i = width / 2; i <= width; i += 2)
	{
		cleardevice();
		putimage(i, 0, &door_r);
		putimage(width / 2 - i, 0, &door_l);
		FlushBatchDraw();
	}
}
//暂停界面
void Pause()
{
	while (1)
	{
		putimagePng(206, 194, &pause);
		if (MouseHit())
		{
			MOUSEMSG m = GetMouseMsg();
			if (m.uMsg == WM_LBUTTONDOWN && fabs(m.y - 263) <= 15)
			{
				if (fabs(m.x - 263) <= 18 && musicon == 1)
				{
					mciSendString("pause bkmusic", NULL, 0, NULL);
					musicon = 0;
				}
				else if (fabs(m.x - 263) <= 18 && musicon == 0)
				{
					mciSendString("resume bkmusic", NULL, 0, NULL);
					musicon = 1;
				}
			}
			if (m.uMsg == WM_LBUTTONDOWN && fabs(m.x - 420) <= 110)
			{
				if (fabs(m.y - 263) <= 30)
					Restart();
				else if (fabs(m.y - 340) <= 30)
					break;
				else if (fabs(m.y - 420) <= 30)
				{
					Door();
					Quit();
				}
			}
		}
		FlushBatchDraw();
	}
}
//胜利界面
void Victory()
{
	while (1)
	{
		putimagePng(228, 158, &victory);
		if (MouseHit())
		{
			MOUSEMSG m = GetMouseMsg();
			if (m.uMsg == WM_LBUTTONDOWN)
			{
				if (fabs(m.x - 421) <= 183 && fabs(m.y - 395) <= 25)
				{
					if (map.lev == 9)
						Quit();
					map.lev++;
					Restart();
				}
				else if (fabs(m.x - 420) <= 65 && fabs(m.y - 466) <= 22)
					Restart();
			}
		}
		FlushBatchDraw();
	}
}
//失败界面
void Lose()
{
	while (1)
	{
		putimagePng(228, 158, &lose);
		if (MouseHit())
		{
			MOUSEMSG m = GetMouseMsg();
			if (m.uMsg == WM_LBUTTONDOWN)
			{
				if (fabs(m.x - 421) <= 183 && fabs(m.y - 395) <= 25)
				{
					if (map.lev == 9)
						Quit();
					Restart();
				}
				else if (fabs(m.x - 420) <= 65 && fabs(m.y - 466) <= 22)
					Restart();
			}
		}
		FlushBatchDraw();
	}
}
//主菜单//选关界面
void Mainmenu()
{
	while (1)//通过跳出循环实现选关
	{
		putimage(0, 0, &mainmenu);//选关界面
		FlushBatchDraw();//批量绘制
		if (MouseHit())
		{
			MOUSEMSG m = GetMouseMsg();//获取鼠标信息
			if (m.uMsg == WM_LBUTTONDOWN)//左键按下时
			{
				if (fabs(m.x - 270) <= 10 && fabs(m.y - 170) <= 20)
					map.lev = 1;
				else if (fabs(m.x - 330) <= 10 && fabs(m.y - 220) <= 20)
					map.lev = 2;
				else if (fabs(m.x - 270) <= 10 && fabs(m.y - 310) <= 20)
					map.lev = 3;
				else if (fabs(m.x - 150) <= 10 && fabs(m.y - 380) <= 20)
					map.lev = 4;
				else if (fabs(m.x - 300) <= 10 && fabs(m.y - 510) <= 20)
					map.lev = 5;
				else if (fabs(m.x - 400) <= 10 && fabs(m.y - 420) <= 20)
					map.lev = 6;
				else if (fabs(m.x - 590) <= 10 && fabs(m.y - 435) <= 20)
					map.lev = 7;
				else if (fabs(m.x - 575) <= 10 && fabs(m.y - 310) <= 20)
					map.lev = 8;
				else if (fabs(m.x - 720) <= 10 && fabs(m.y - 295) <= 20)
					map.lev = 9;
				else
					continue;
				Sleep(1000);//短暂延时
				break;//跳出循环进入关卡
			}
		}
	}
}
//游戏中//用于重新开始本关
void Restart()
{
	Door();
	map.Start();//每一关初始化、载入资源
	map.Draw();
	gif = 0;
	while (1)
	{
		UpdateWithInput();//与输入有关的更新
		UpdateWithoutInput();//与输入无关的更新	
		FlushBatchDraw();
		gif++;
	}
}
//游戏主体//用于return重新选关
void Quit()
{
	while (1)
	{
		Mainmenu();//主菜单界面
		Restart();
	}
}
//主函数
int main()
{
	Start();//初始化、载入资源
	Quit();
	system("pause");
	closegraph;//关闭画布
	return 0;
}